/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.ac.iie.di.wx.mongoservice;

import cn.ac.iie.di.wx.common.RuntimeEnv;
import cn.ac.iie.di.wx.startup.Server;
import cn.ac.iie.di.wx.utils.GenerateMD5Util;
import static cn.ac.iie.di.wx.utils.GenerateMD5Util.string2MD5;
import cn.ac.iie.di.wx.utils.IDSHAGeneratorImpl;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.Block;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.QueryOperators;
import com.mongodb.ServerAddress;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.apache.log4j.Logger;
import org.bson.Document;
import org.bson.conversions.Bson;

/**
 *
 * @author zwlin
 */
public class MongoHelper {

    private static final Logger logger = Logger.getLogger(MongoHelper.class.getName());

    public MongoClient conn;
    public MongoDatabase db;
    public MongoCollection<Document> collection;

    String[] retFieldNames;

//    public static void main(String[] args) {
//        try {
//            String[] host = new String[1];
//            host[0] = "10.136.139.21:23456";
//
//            MongoHelper mongoHelper = new MongoHelper(host, "docinfo", "docinfo", "w123", "w123");
//            //条件查询
//            String _id = "40M-WX01-A_WeChat_05f058017016090f09d0b90de0ac0930108d03203d09e";
//            String _id2 = "40M-WX01-A_WeChat_05f058017016090f09d0b90de0ac0930108d032031111";
////            String re=mongoHelper.find("_id", _id);
//            String re2 = mongoHelper.find("_id", _id2);
//            String mm = "/gmsg/19700118/000848/06407a0870c80cf0a20440fa09a0be0980e608803f03a07c.jpg";
//            String ww = re2 + "/" + GenerateMD5Util.string2MD5("http://mmbiz.qpic.cn/mmbiz_gif/HspM38w6GMdSicIRSEgvwJw65oy8l0YFrbkmzibKn7lvl7wLNkxMCjehibWhicdJFT8XgiaJ3bF1KK1bSgnguAT6ABw/0?wx_fmt=gif") + ".jpg";
//            if (mm.equals(ww)) {
//                logger.info("eq");
//            }
//            FindIterable<Document> ft = mongoHelper.collection.find(new Document("_id", _id2));
////db.things.find( { a : { $exists : true } } ); // 如果存在元素a,就返回
////db.things.find( { a : { $exists : false } } ); // 如果不存在元素a，就返回
//            ft.forEach(new Block<Document>() {
//                @Override
//                public void apply(Document t) {
////                    logger.info(t.toJson());
//                    String tjson = t.toJson();
//                    JSONObject userInfoOb = JSONObject.parseObject(tjson);
//                    String[] nrd = userInfoOb.getObject("nrd", String[].class);
//                    String wxUrl = nrd[0];
//                    logger.info(wxUrl);
//                    String wxUrlHead = wxUrl.substring(0, wxUrl.lastIndexOf("/"));
//                    logger.info(wxUrlHead);
//                    //还需要添加/，因为substring的时候去掉了
////                    Set<Map.Entry<String, Object>> es = userInfoOb.entrySet();
////                    for (Iterator<Map.Entry<String, Object>> iterator = es.iterator(); iterator.hasNext();) {
////                        Map.Entry<String, Object> next = iterator.next();
////                        logger.info(next.getKey() + "\t" + next.getValue());
////                    }
//                }
//            });
//            boolean is = mongoHelper.PicIsExist(_id2);
//            logger.info(is);
//        } catch (Exception ex) {
//            logger.error(ex.getMessage(), ex);
//        }
//    }
    //测试更新粉丝数
//    public static void main(String[] args) throws Exception {
//        Server.init();
//        String[] host = new String[1];
//        host[0] = (String) RuntimeEnv.getParam(RuntimeEnv.WXUSERNEWMONGOHOSTS);
//
//        MongoHelper mongoHelper = new MongoHelper(host,
//                (String) RuntimeEnv.getParam(RuntimeEnv.WXUSERNEWDBNAME),
//                (String) RuntimeEnv.getParam(RuntimeEnv.WXUSERNEWTABLENAME),
//                (String) RuntimeEnv.getParam(RuntimeEnv.WXUSERNEWUSERNAME),
//                (String) RuntimeEnv.getParam(RuntimeEnv.WXUSERNEWPASSWORD));
//        HashMap<String, Object> hashMap = new HashMap<String, Object>();
//        hashMap.put("40U-WX03-A_WeChat_6b3636891c193a4e6af04d66f8f7dfe1977f0114", Long.parseLong("3357"));
//        hashMap.put("40U-WX03-A_WeChat_e882997d911a0427a0735844c307451c3511ab88", Long.parseLong("936"));
//        hashMap.put("40U-WX03-A_WeChat_3c3ec0d48996c3be3e99a0200b40cbc0d1780e17", Long.parseLong("34"));
//        hashMap.put("40U-WX03-A_WeChat_0bfc628744f6275833e93226953ee77d63ed11bd", Long.parseLong("11"));
//        hashMap.put("40U-WX03-A_WeChat_0e2f24a3edbefd4b85df3a1ed9a62c12aba2a451", Long.parseLong("9"));
//
//        mongoHelper.update("_id", "nfans", hashMap);
//        ArrayList<String> collection = new ArrayList<>();
//        collection.add("40U-WX03-A_WeChat_6b3636891c193a4e6af04d66f8f7dfe1977f0114");
//        collection.add("40U-WX03-A_WeChat_e882997d911a0427a0735844c307451c3511ab88");
//        collection.add("40U-WX03-A_WeChat_3c3ec0d48996c3be3e99a0200b40cbc0d1780e17");
//        collection.add("40U-WX03-A_WeChat_0bfc628744f6275833e93226953ee77d63ed11bd");
//        collection.add("40U-WX03-A_WeChat_0e2f24a3edbefd4b85df3a1ed9a62c12aba2a451");
//
//        mongoHelper.find("_id", collection);
////        hashMap.put(IDSHAGeneratorImpl.getId("40", "U-WX03-A", "WeChat", "WeChat", "fb5b0e2d214f9676ae2cf9e0f97adacddcb00e7c"), "zgzxxzs");
////        logger.info(hashMap.isEmpty());
////        mongoHelper.update("_id", "uid", hashMap);
//
////        ArrayList<String> collection=new ArrayList<>();
////        String url="http://mp.weixin.qq.com/s?__biz=MzA5OTUwNTcyMA==&mid=2651937447&idx=1&sn=48392c52e3f6588edfd894bb0e9c9e01&chksm=8b64b9f0bc1330e6a9ef510e92b2d731e0af4be8bf625fe33c6702129f2548ad528741992b18&scene=309999#rd";
////        String url_md5=string2MD5(url);
////        
////        collection.add(url_md5);
////        HashMap<String, Object> rnt = mongoHelper.find("urlmd5", collection);
////        for (Map.Entry<String, Object> entrySet : rnt.entrySet()) {
////            String key = entrySet.getKey();
////            Object value = entrySet.getValue();
////            logger.info(key);
////            logger.info(value);
////            entrySet.setValue("30");
////            hashMap.put(key, "20");
////        }
////        mongoHelper.update("_id", "nrd", rnt);
////        HashMap<String, Object> rnt_1 = mongoHelper.find("urlmd5", collection);
////        for (Map.Entry<String, Object> entrySet : rnt_1.entrySet()) {
////            String key = entrySet.getKey();
////            Object value = entrySet.getValue();
////            logger.info(key);
////            logger.info(value);
////        }
//    }
    public static void main(String[] args) throws Exception {
        logger.info("start 1");
        Server.init();
        String[] host = new String[1];
        host[0] = (String) RuntimeEnv.getParam(RuntimeEnv.WXMSGNEWMONGOHOSTS);

        MongoHelper mongoHelper = new MongoHelper(host,
                (String) RuntimeEnv.getParam(RuntimeEnv.WXMSGNEWDBNAME),
                (String) RuntimeEnv.getParam(RuntimeEnv.WXMSGNEWTABLENAME_GROUP),
                (String) RuntimeEnv.getParam(RuntimeEnv.WXMSGNEWUSERNAME),
                (String) RuntimeEnv.getParam(RuntimeEnv.WXMSGNEWPASSWORD));
//        HashMap<String, Object> hashMap = new HashMap<String, Object>();
        ArrayList<String> collection = new ArrayList<>();
        collection.add("40M-WX01-A_WeChat_b3a3ffc9d007de61389edd70c4dfd161");
        logger.info("start 2");
        HashMap<String, Object> rnt = mongoHelper.findLfn("_id", collection);
        for (Map.Entry<String, Object> entrySet : rnt.entrySet()) {
            String key = entrySet.getKey();
            Object value = entrySet.getValue();
            logger.info(key);
//            logger.info(value);
            String[] wx_lfn = (String[]) value;
            for (String wx_lfn1 : wx_lfn) {
                logger.info(wx_lfn1);
            }
//            entrySet.setValue("30");
        }
    }

    public MongoHelper(String[] hosts, String dbname, String tablename, String username, String passwd) throws Exception {
        try {
            List<ServerAddress> seeds = new ArrayList<>();
            for (String host : hosts) {
                String[] serv = host.split(":");
                if (serv != null && serv.length == 2) {
                    seeds.add(new ServerAddress(serv[0], Integer.parseInt(serv[1])));
                }
            }
            MongoCredential credential = MongoCredential.createScramSha1Credential(username, dbname, passwd.toCharArray());
            List<MongoCredential> credentialsList = new ArrayList<MongoCredential>();
            credentialsList.add(credential);

            conn = new MongoClient(seeds, credentialsList);
            db = conn.getDatabase(dbname);
            collection = db.getCollection(tablename);
        } catch (Exception ex) {
            try {
                conn.close();
            } catch (Exception ex1) {
            }
            throw new Exception("cacnnot init mongo helper !", ex);

        }
    }

    public static Document getDocument(HashMap<String, Object> rec) {
        Document doc = new Document();
        for (Map.Entry<String, Object> entry : rec.entrySet()) {
            Object v = entry.getValue();
            if (v.getClass().isArray()) {
                int n = Array.getLength(v);
                List l = new ArrayList(n);
                for (int i = 0; i < n; ++i) {
                    l.add(Array.get(v, i));
                }
                doc.put(entry.getKey(), l);
            } else {
                doc.put(entry.getKey(), v);
            }
        }
        return doc;
    }
//根据key列名和对应的值条件查询库中一条记录，截取其中字段的值

    public String find(String key, String value) {
        String result = "";
        FindIterable<Document> ft = collection.find(new Document(key, value));
        for (Document t : ft) {
            result = t.toJson();
//            JSONObject userInfoOb = JSONObject.parseObject(tjson);
//            String[] nrd = userInfoOb.getObject("nrd", String[].class);
//            String wxUrl = nrd[0];
//            result = wxUrl.substring(0, wxUrl.lastIndexOf("/"));
            logger.info(result);
        }
        return result;
    }
    //判断图片是否存在

//    public Set<String> find(String keyCol, Set<String> mongoPicUrl) {
//        Set<String> returnUidSet = new HashSet<String>();//存放返回的uid
//        Set<String> resultUidSet = new HashSet<String>();//存放req和req2不同的uid
//        //若是查不到该条数据，则集合会比输入集合小，若全部查不到，则返回为空
//        MongoCursor<Document> cursor = collection.find(new Document(keyCol, new BasicDBObject(QueryOperators.IN, mongoPicUrl))).iterator();
//        while (cursor.hasNext()) {
//            Document rec = cursor.next();//一条记录，包括34个字段
//            returnUidSet.add(rec.getString("uid"));//通过索引获取mongo中的id暂时写成这个uid，判断图片是否存在
//            //先把值取出来，然后再对比
//        }
//        //遍历原始uid集合，如果uid能够添加到从mongo库中查询的集合中，说明此uid在mong中没有对应的记录，则将其添加到返回集合中
//        for (String req : mongoPicUrl) {
//            if (returnUidSet.add(req)) {
//                resultUidSet.add(req);
//            }
//        }
//        return resultUidSet;
//    }
    public boolean PicIsExist(String picUrl) {
        boolean existFlag = false;//默认不存在
//        FindIterable<Document> exist = collection.find(new BasicDBObject(picUrl, new BasicDBObject(
//                "$exists", true)));
        FindIterable<Document> exist = collection.find(new BasicDBObject().append(picUrl, new BasicDBObject().append("$exists", false)));
        for (Document t : exist) {
            String tjson = t.toJson();

            logger.info(tjson);
        }
        return existFlag;
    }

    /**
     * 批量查询
     *
     * @param keyCol	用来定位记录的列名
     * @param keys	用来定位记录的列值集合
     * @param retFields	返回的列名,所有列名，即批量查询的结果是返回该条完整记录
     * @return
     */
    public List<HashMap<String, Object>> find(String keyCol, Collection<String> keys, String[] retFields) {
        List<HashMap<String, Object>> rtn = new ArrayList<>(keys.size());
        MongoCursor<Document> cursor = collection.find(new Document(keyCol, new BasicDBObject(QueryOperators.IN, keys))).iterator();
        while (cursor.hasNext()) {
            Document rec = cursor.next();
            HashMap<String, Object> rtnRec = new HashMap<>(retFields.length);
            for (String str : retFields) {
                rtnRec.put(str, rec.get(str));
            }
            rtn.add(rtnRec);
        }
        return rtn;
    }

    //未测试是否准确
    public HashMap<String, Object> find(String keyCol, Collection<String> keys) {
        HashMap<String, Object> rtn = new HashMap<>(keys.size());
        MongoCursor<Document> cursor = collection.find(new Document(keyCol, new BasicDBObject(QueryOperators.IN, keys))).iterator();
        while (cursor.hasNext()) {
            Document rec = cursor.next();
            String tjson = rec.toJson();
            JSONObject userInfoOb = JSONObject.parseObject(tjson);
            String _id = userInfoOb.getObject("_id", String.class);

            rtn.put(_id, "");
        }
        return rtn;
    }

    public HashMap<String, Object> findLfn(String keyCol, Collection<String> keys) {
        HashMap<String, Object> rtn = new HashMap<>(keys.size());
        MongoCursor<Document> cursor = collection.find(new Document(keyCol, new BasicDBObject(QueryOperators.IN, keys))).iterator();
        while (cursor.hasNext()) {
            Document rec = cursor.next();
            String tjson = rec.toJson();
//            logger.info(tjson);
            JSONObject userInfoOb = JSONObject.parseObject(tjson);
            String _id = userInfoOb.getObject("_id", String.class);
            String[] wx_lfn = userInfoOb.getObject("wx_lfn", String[].class);
            rtn.put(_id, wx_lfn[0]);

        }
        return rtn;
    }

    /**
     * 批量更新，将某一列的值增加一
     *
     * @param keyCol	用于定位记录的列名
     * @param keys	用于定位记录的列值集合
     * @param incCol	需要加一的列名
     */
    public void upinc(String keyCol, Collection<String> keys, String incCol) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        for (String key : keys) {
            Document doc = new Document();
            doc.put(incCol, 1L);
            updatedoc.add(
                    new UpdateOneModel<Document>(new Document(keyCol, key),
                            new Document("$inc", doc), new UpdateOptions().upsert(false)));
        }
        collection.bulkWrite(updatedoc);
    }

    /**
     * 批量更新，将某一列的值增加一定数值,只能是数字类型的数据进行更新 。
     *
     * @param keyCol	用于定位记录的列名
     * @param updateCol	用于增加的列名
     * @param updateValues	keyCol的值和updateCol的增量的对应关系
     */
    public void upadd(String keyCol, String updateCol, HashMap<String, Long> updateValues) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        for (Map.Entry<String, Long> entry : updateValues.entrySet()) {
            Document doc = new Document();
            doc.put(updateCol, entry.getValue());
            updatedoc.add(
                    new UpdateOneModel<Document>(new Document(keyCol, entry.getKey()),
                            new Document("$inc", doc), new UpdateOptions().upsert(false)));
        }
        collection.bulkWrite(updatedoc);
    }

    /**
     * 批量更新，将某一列的值更新为新值。 $set用来指定一个键并更新键值，若键不存在并创建
     *
     * @param keyCol	用于定位记录的列名
     * @param updateCol	用于更新的列名
     * @param updateValues	keyCol的值和updateCol的增量的对应关系
     */
    public void update(String keyCol, String updateCol, HashMap<String, Object> updateValues) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        for (Map.Entry<String, Object> entry : updateValues.entrySet()) {
            Document doc = new Document();
            doc.put(updateCol, entry.getValue());
            updatedoc.add(
                    new UpdateOneModel<Document>(new Document(keyCol, entry.getKey()),
                            new Document("$set", doc), new UpdateOptions().upsert(false)));
        }
        BulkWriteResult bulkWrite = collection.bulkWrite(updatedoc);
        if (bulkWrite.getMatchedCount() != 0) {
            logger.info("update to mongo " + bulkWrite.getMatchedCount());
        }

    }
    /*
     $set用来指定一个键并更新键值，若键不存在并创建
     */

    public void add(String keyCol, String key, InputStream picInputStream) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        Document doc = new Document();
        doc.put(key, picInputStream);
        updatedoc.add(
                new UpdateOneModel<Document>(new Document(keyCol, key),
                        new Document("$set", doc), new UpdateOptions().upsert(false)));
        collection.bulkWrite(updatedoc);
    }

    public void add(String keyCol, String key, File file) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        Document doc = new Document();
        doc.put(key, file);
        updatedoc.add(
                new UpdateOneModel<Document>(new Document(keyCol, key),
                        new Document("$set", doc), new UpdateOptions().upsert(false)));
        collection.bulkWrite(updatedoc);
    }

    public void batchAdd(String keyCol, ConcurrentHashMap<String, InputStream> concurrentHashMap) {
        List<WriteModel<Document>> updatedoc = new ArrayList<>();
        Document doc = new Document();
        for (Map.Entry<String, InputStream> entrySet : concurrentHashMap.entrySet()) {
            doc.put(entrySet.getKey(), entrySet.getValue());
            updatedoc.add(
                    new UpdateOneModel<Document>(new Document(keyCol, entrySet.getKey()),
                            new Document("$set", doc), new UpdateOptions().upsert(false)));
        }
        collection.bulkWrite(updatedoc);
    }
}
